package com.gupaoedu.vip.pattern.springmvc2.context;

import com.gupaoedu.vip.pattern.springmvc1.annotation.MyAutowired;
import com.gupaoedu.vip.pattern.springmvc1.annotation.MyController;
import com.gupaoedu.vip.pattern.springmvc1.annotation.MyService;
import com.gupaoedu.vip.pattern.springmvc2.aop.MyAopProxy;
import com.gupaoedu.vip.pattern.springmvc2.aop.MyCglibAopProxy;
import com.gupaoedu.vip.pattern.springmvc2.aop.MyJDKDaynamicAopProxy;
import com.gupaoedu.vip.pattern.springmvc2.aop.config.MyAopConfig;
import com.gupaoedu.vip.pattern.springmvc2.aop.support.MyAdvisedSupport;
import com.gupaoedu.vip.pattern.springmvc2.beans.MyBeanFactory;
import com.gupaoedu.vip.pattern.springmvc2.beans.config.MyBeanWrapper;
import com.gupaoedu.vip.pattern.springmvc2.beans.config.MyBeandefinition;
import com.gupaoedu.vip.pattern.springmvc2.beans.support.MyBeanDefinitionReader;
import com.gupaoedu.vip.pattern.springmvc2.beans.support.MyDefaultListableBeanFactory;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class MyApplicationContext extends MyDefaultListableBeanFactory implements MyBeanFactory {

    private String [] configLocations;

    private MyBeanDefinitionReader reader;

    //单例的IOC容器缓存
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();

    //通用的IOC容器
    private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, MyBeanWrapper>();

    public MyApplicationContext(String... configLocations){
        this.configLocations = configLocations;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void refresh() throws Exception {
       // TODO 定位  找到配置文件
        reader = new MyBeanDefinitionReader(configLocations);
       // TODO 加载 扫描相关文件，把他们封装成BeanDefinition
        List<MyBeandefinition> beanDefinitions = reader.loadBeanDefinitions();
       // TODO 注册 把配置信息放到容器里面(伪IOC容器)
        doRegisterBeanDefinition(beanDefinitions);
       // TODO 把不是延迟加载的类 提前初始化
        doAutowired();
    }

    private void doAutowired() {
        for (Map.Entry<String, MyBeandefinition> beandefinitionEntry : super.beandefinitionMap.entrySet()) {
            String beanName = beandefinitionEntry.getKey();
            if(!beandefinitionEntry.getValue().isLazyInit()){
                getBean(beanName);
            }
        }

    }

    private void doRegisterBeanDefinition(List<MyBeandefinition> beanDefinitions) throws Exception {
        for (MyBeandefinition beanDefinition:beanDefinitions) {
            if(super.beandefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("The “"+beanDefinition.getFactoryBeanName()+"“ is exits");
            }
            super.beandefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    public Object getBean(String beanName) {
        MyBeandefinition beandefinition = super.beandefinitionMap.get(beanName);

        // TODO 初始化
        MyBeanWrapper beanWrapper = instantiateBean(beanName,beandefinition);
        // TODO 拿到BeanWrapper 保存到IOC容器中
        this.factoryBeanInstanceCache.put(beanName, beanWrapper);
        // TODO 注入
        populateBean(beanName,beandefinition,beanWrapper);
        return this.factoryBeanInstanceCache.get(beanName);
    }

    private void populateBean(String beanName, MyBeandefinition myBeandefinition, MyBeanWrapper beanWrapper) {
        Object instance = beanWrapper.getWrappedInstance();

        Class<?> clazz = beanWrapper.getWrappedClass();
        if(!(clazz.isAnnotationPresent(MyController.class)||clazz.isAnnotationPresent(MyService.class))){return;}
        // TODO 获取所有 fields
        Field [] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if(!field.isAnnotationPresent(MyAutowired.class)){continue;}
            MyAutowired autowired = field.getAnnotation(MyAutowired.class);
            String autowiredBeanName = autowired.value().trim();
            if("".equals(autowiredBeanName)){
                autowiredBeanName = field.getType().getName();
            }
            field.setAccessible(true);
            try {
                if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){continue;}
                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    }

    private MyBeanWrapper instantiateBean(String beanName, MyBeandefinition myBeandefinition) {
        // TODO 拿到实例化的对象的类名
        String className = myBeandefinition.getBeanClassName();
        // TODO 反射实例化 得到一个对象
        Object instance = null;
        try {
            //假设默认是单例
            if(this.singletonObjects.containsKey(className)){
                instance = this.singletonObjects.get(className);
            }else {
                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();
                MyAdvisedSupport config = instantionAopConfig(myBeandefinition);
                config.setTargetClass(clazz);
                config.setTarget(instance);
                //符合pointcut 的规则，创建代理对象
                if(config.pointMatch()){
                    instance = createProxy(config).getProxy();
                }
                this.singletonObjects.put(className, instance);
                this.singletonObjects.put(myBeandefinition.getFactoryBeanName(), instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // TODO 把这个对象存到BeanWrapper
        MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);
        // TODO 把BeanWrapper存到IOC容器中

        return beanWrapper;
    }

    private MyAopProxy createProxy(MyAdvisedSupport config) {
        Class targetClass = config.getTargetClass();
        if(targetClass.getInterfaces().length>0){
            return new MyJDKDaynamicAopProxy(config);
        }
        return new MyCglibAopProxy(config);
    }

    private MyAdvisedSupport instantionAopConfig(MyBeandefinition myBeandefinition) {
        MyAopConfig config = new MyAopConfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new MyAdvisedSupport(config);
    }

    public String [] getBeanDefinitionNames(){
        return this.beandefinitionMap.keySet().toArray(new String[this.beandefinitionMap.size()]);
    }

    public int getBeanDefinitionCount(){
        return this.beandefinitionMap.size();
    }

    public Properties getConfig(){
        return this.reader.getConfig();
    }
}
